package w.z.x.framework.repository.factory;

import com.alibaba.fastjson.JSON;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;
import w.z.x.framework.repository.annotations.*;
import w.z.x.framework.repository.enums.Fill;
import w.z.x.framework.repository.interfaces.Repository;
import w.z.x.framework.repository.interfaces.SerialFunction;
import w.z.x.framework.repository.structures.BeanHelper;
import w.z.x.framework.repository.structures.Page;
import w.z.x.framework.repository.structures.Segment;
import w.z.x.framework.repository.wrappers.LambdaDeleteWrapper;
import w.z.x.framework.repository.wrappers.LambdaQueryWrapper;
import w.z.x.framework.repository.wrappers.LambdaUpdateWrapper;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.math.BigDecimal;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * MYSQL工厂
 *
 * @author 魏祖潇
 */
@Slf4j
@ConditionalOnClass(name = "org.springframework.jdbc.core.JdbcTemplate")
public class MysqlFactory implements Repository {
    private static final String LIMITER = "`";
    private static final String D = "$";
    private static final String SIMPLE_NAME = "`%s`";
    private static final String ALL_NAME = "$0.`%s`";
    private static final String WHERE = "where";
    private static final String OR = "or";
    private static final String AND = "and";
    private static final String JOIN = "join";
    private static final String LIKE = "like";
    private static final String NOT_LIKE = "not like";
    private static final String EQ = "=";
    private static final String IN = "in";
    private static final String SET = "set";
    private static final String QUESTION = "?";
    private static final String EQ_QUESTION = " = ?";
    private static final String ORDER_BY = "order by";
    private static final String EMPTY = "";
    private static final String SINGLE_STRING = " ";
    private static final String ASC = "asc";
    private static final String DESC = "desc";
    private static final String LEFT = "(";
    private static final String RIGHT = ")";
    private static final String SELECT = "select";
    private static final String UPDATE = "update";
    private static final String DELETE = "delete";
    private static final String INSERT = "insert into";
    private static final String FROM = "from";
    private static final String AS = " as ";
    private static final String LIMIT = "limit";
    private static final String COMMA = ", ";
    private static final String ADD_ONE = "+ 1";
    private static final String COUNT = "count(1)";
    private static final String T = "$t";
    private static final String GET = "get";
    private static final String IS = "is";
    private static final String ON = "on";
    private static final String VALUES = "values(";
    private static final String PER = "%";

    private static JdbcTemplate master;

    private static JdbcTemplate slave;

    /**
     * 载入主库
     *
     * @param jdbcTemplate jdbcTemplate
     */
    @Resource
    private void initJdbcTemplate(final JdbcTemplate jdbcTemplate) {
        MysqlFactory.master = jdbcTemplate;
    }

    /**
     * 载入从库
     *
     * @param jdbcTemplate slaveJdbcTemplate
     */
    @Resource(name = "slaveJdbcTemplate")
    private void initSlaveJdbcTemplate(final JdbcTemplate jdbcTemplate) {
        MysqlFactory.slave = jdbcTemplate;
    }

    /**
     * 构建
     *
     * @param clazz 类型
     * @param <E>   泛型
     * @return 实例
     */
    @SneakyThrows
    public static <E extends MysqlFactory> E build(Class<E> clazz) {
        return clazz.newInstance();
    }

    /**
     * 属性
     *
     * @param field 字段
     * @param value 值
     * @param <E>   泛型
     * @return 实例
     */
    @SneakyThrows
    public <E extends MysqlFactory> E of(SerialFunction<E, ?> field, Object value) {
        BeanHelper helper = new BeanHelper(this.getClass());
        Collection<Method> methods = helper.getMethods();
        Optional<Method> opt = methods.stream()
                .filter(m -> m.getName().equals(SET + BeanHelper.toFirstUpperCase(SerialFunction.field(field).getName())))
                .findFirst();
        if (!opt.isPresent()) {
            throw new RuntimeException("无法操作内部属性");
        }
        opt.get().setAccessible(true);
        opt.get().invoke(this, value);
        return (E) this;
    }

    /**
     * 条件查询
     *
     * @param clazz   对象
     * @param wrapper 查询封装
     * @param <E>     泛型
     * @return 实体
     */
    public static <E extends MysqlFactory> E get(Class<E> clazz, LambdaQueryWrapper<E> wrapper) {
        wrapper.offset(0);
        wrapper.limit(1);
        List<E> list = find(clazz, wrapper);
        return Objects.nonNull(list) && !list.isEmpty() ? list.get(0) : null;
    }

    /**
     * 单个实例查询
     *
     * @param clazz 类型
     * @param id    主键
     * @param <E>   泛型
     * @return 实例
     */
    public static <E extends MysqlFactory> E get(Class<E> clazz, Object id) {
        List<E> list = get(clazz, new Object[]{id});
        return Objects.isNull(list) || list.isEmpty() ? null : list.get(0);
    }

    /**
     * 实例查询
     *
     * @param clazz 类
     * @param id    ID
     * @param <E>   泛型
     * @return 列表
     */
    public static <E extends MysqlFactory> List<E> get(Class<E> clazz, Object... id) {
        BeanHelper helper = new BeanHelper(clazz);
        Mapper mapper = helper.getMapper();
        if (Objects.isNull(mapper)) {
            throw new RuntimeException("对象未与存储结构进行映射");
        }
        Collection<Field> fields = helper.getFields();
        Collection<Field> columns = fields.stream().filter(f ->
                !f.isAnnotationPresent(Transient.class)
                        && !Modifier.isStatic(f.getModifiers())
                        && !Modifier.isTransient(f.getModifiers())
                        && !Modifier.isFinal(f.getModifiers())
        ).collect(Collectors.toList());
        if (Objects.isNull(columns) || columns.isEmpty()) {
            throw new RuntimeException("无法解析空对象结构");
        }
        Optional<Field> idField = columns.stream()
                .filter(f -> f.isAnnotationPresent(ID.class))
                .findFirst();
        if (!idField.isPresent()) {
            throw new RuntimeException("缺少ID注解");
        }
        Optional<Field> deleteField = columns.stream()
                .filter(f -> f.isAnnotationPresent(LogicDelete.class))
                .findFirst();
        Segment where = Segment.build(null, AND, null);
        if (deleteField.isPresent()) {
            Object deleted = 0;
            Segment deletedSegment = Segment.build(deleteField.get(), EQ, deleted);
            where.append(deletedSegment);
        }
        Segment condition = Segment.build(
                idField.get(),
                id.length == 1 ? EQ : IN,
                Arrays.asList(id)
        );
        where.append(condition);
        StringJoiner sj = new StringJoiner(SINGLE_STRING)
                .add(select(mapper, columns, false))
                .add(WHERE)
                .add(where(mapper, where, false));
        Collection<Object> params = params(where);
        assert params != null;
        List<E> list = find(clazz, sj.toString(), params.toArray());
        if (Objects.isNull(list) || list.isEmpty()) {
            return Collections.EMPTY_LIST;
        }
        return list;
    }

    /**
     * 查询
     *
     * @param clazz   类型
     * @param wrapper 封装
     * @param <E>     泛型
     * @return 列表
     */
    public static <E extends MysqlFactory> List<E> find(Class<E> clazz, LambdaQueryWrapper<E> wrapper) {
        BeanHelper helper = new BeanHelper(clazz);
        Mapper mapper = helper.getMapper();
        if (Objects.isNull(mapper)) {
            throw new RuntimeException("对象未与存储结构进行映射");
        }
        Collection<Field> fields = helper.getFields();
        Collection<Field> columns = fields.stream().filter(f ->
                !f.isAnnotationPresent(Transient.class)
                        && !Modifier.isStatic(f.getModifiers())
                        && !Modifier.isTransient(f.getModifiers())
                        && !Modifier.isFinal(f.getModifiers())
        ).collect(Collectors.toList());
        if (columns.isEmpty()) {
            throw new RuntimeException("无法解析空对象结构");
        }
        Segment where = wrapper.whereSegment();
        Optional<Field> deleteField = columns.stream()
                .filter(f -> f.isAnnotationPresent(LogicDelete.class))
                .findFirst();
        if (deleteField.isPresent()) {
            if (Objects.isNull(where)) {
                where = Segment.build(null, AND, null);
            }
            Segment deletedSegment = Segment.build(deleteField.get(), EQ, 0);
            where.append(deletedSegment);
        }
        StringJoiner sj = new StringJoiner(SINGLE_STRING);
        sj.add(select(mapper, columns, !wrapper.joinSegment().isEmpty()));
        if (!wrapper.joinSegment().isEmpty()) {
            String joiners = IntStream.range(0, wrapper.joinSegment().size())
                    .mapToObj(i -> join(mapper, wrapper.joinSegment().get(i), i + 1))
                    .collect(Collectors.joining(SINGLE_STRING));
            sj.add(joiners);
        }
        if (Objects.nonNull(where) && Objects.nonNull(where.getChildren()) && !where.getChildren().isEmpty()) {
            sj.add(WHERE).add(where(mapper, where, !wrapper.joinSegment().isEmpty()));
        }
        if (!wrapper.orderBySegment().isEmpty()) {
            sj.add(ORDER_BY).add(orderBy(mapper, wrapper.orderBySegment(), !wrapper.joinSegment().isEmpty()));
        }
        if (wrapper.limit() > 0) {
            sj.add(LIMIT);
            if (wrapper.offset() > 0) {
                sj.add(QUESTION).add(COMMA);
            }
            sj.add(QUESTION);
        }
        Collection<Object> params = new ArrayList<>();
        if (!wrapper.joinSegment().isEmpty()) {
            wrapper.joinSegment().stream().forEach(it -> {
                params.addAll(params(it));
            });
        }
        if (Objects.nonNull(where)) {
            params.addAll(params(where));
        }
        if (wrapper.limit() > 0) {
            if (wrapper.offset() > 0) {
                params.add(wrapper.offset());
            }
            params.add(wrapper.limit());
        }
        List<E> list = find(clazz, sj.toString(), params.toArray());
        if (Objects.isNull(list) || list.isEmpty()) {
            return Collections.EMPTY_LIST;
        }
        return list;
    }

    /**
     * sql查询
     *
     * @param sql    sql语句
     * @param params 查询参数
     * @return 列表
     */
    public static List<Map<String, Object>> find(String sql, Object... params) {
        return slave.queryForList(sql, params);
    }

    /**
     * sql查询
     *
     * @param toConvert 转化类型
     * @param sql       sql语句
     * @param params    查询参数
     * @param <E>       泛型
     * @return 列表
     */
    public static <E> List<E> find(Class<E> toConvert, String sql, Object... params) {
        List<Map<String, Object>> list = find(sql, params);
        return write(toConvert, list);
    }

    /**
     * count
     *
     * @param clazz 类型
     * @param <E>   泛型
     * @return 数量
     */
    public static <E extends MysqlFactory> Long count(Class<E> clazz) {
        return count(clazz, new LambdaQueryWrapper<>());
    }

    /**
     * count
     *
     * @param sql    sql
     * @param params 参数
     * @return 数量
     */
    public static Long count(String sql, Object... params) {
        return slave.queryForObject(sql, Long.class, params);
    }

    /**
     * count
     *
     * @param clazz   类型
     * @param wrapper 查询封装
     * @param <E>     泛型
     * @return 数量
     */
    public static <E extends MysqlFactory> Long count(Class<E> clazz, LambdaQueryWrapper<E> wrapper) {
        BeanHelper helper = new BeanHelper(clazz);
        Mapper mapper = helper.getMapper();
        if (Objects.isNull(mapper)) {
            throw new RuntimeException("对象未与存储结构进行映射");
        }
        Collection<Field> fields = helper.getFields();
        Optional<Field> deleteField = fields.stream()
                .filter(f -> !Modifier.isStatic(f.getModifiers())
                        && !Modifier.isTransient(f.getModifiers())
                        && !Modifier.isFinal(f.getModifiers())
                )
                .filter(f -> f.isAnnotationPresent(LogicDelete.class))
                .findFirst();
        Segment where = wrapper.whereSegment();
        if (deleteField.isPresent()) {
            if (Objects.isNull(where)) {
                where = Segment.build(null, AND, null);
            }
            Segment deletedSegment = Segment.build(deleteField.get(), EQ, 0);
            where.append(deletedSegment);
        }
        StringJoiner sj = new StringJoiner(SINGLE_STRING)
                .add(SELECT)
                .add(COUNT)
                .add(FROM)
                .add(!wrapper.joinSegment().isEmpty() ? String.format(ALL_NAME, mapper.value()) : String.format(SIMPLE_NAME, mapper.value()));
        if (!wrapper.joinSegment().isEmpty()) {
            String joiners = IntStream.range(0, wrapper.joinSegment().size())
                    .mapToObj(i -> join(mapper, wrapper.joinSegment().get(i), i + 1))
                    .collect(Collectors.joining(SINGLE_STRING));
            sj.add(joiners);
        }
        if (Objects.nonNull(where) && Objects.nonNull(where.getChildren()) && !where.getChildren().isEmpty()) {
            sj.add(WHERE).add(where(mapper, where, !wrapper.joinSegment().isEmpty()));
        }
        Collection<Object> params = new ArrayList<>();
        if (!wrapper.joinSegment().isEmpty()) {
            wrapper.joinSegment().stream().forEach(it -> {
                params.addAll(params(it));
            });
        }
        if (Objects.nonNull(where)) {
            params.addAll(params(where));
        }
        return count(sj.toString(), params.toArray());
    }

    /**
     * 分页
     *
     * @param current 当前页码
     * @param size    每页条数
     * @param sql     sql
     * @param params  参数
     * @return 分页
     */
    public static Page<Map<String, Object>> page(int current, int size, String sql, Object... params) {
        StringJoiner sj = new StringJoiner(SINGLE_STRING)
                .add(SELECT)
                .add(COUNT)
                .add(FROM)
                .add(LEFT)
                .add(sql)
                .add(RIGHT)
                .add(T);
        Long total = count(sj.toString(), params);
        sj = new StringJoiner(SINGLE_STRING)
                .add(sql)
                .add(LIMIT)
                .add(QUESTION)
                .add(COMMA)
                .add(QUESTION);
        List<Object> list = new ArrayList<>();
        if (Objects.nonNull(params)) {
            IntStream.range(0, params.length).forEach(i -> list.add(params[i]));
        }
        list.add((current - 1) * size);
        list.add(size);
        List<Map<String, Object>> rows = find(sj.toString(), list.toArray());
        return Page.convert(current, size, total, rows);
    }

    /**
     * 分页查询
     *
     * @param toConvert 类
     * @param current   当前页码
     * @param size      每页条数
     * @param sql       查询语句
     * @param params    查询参数
     * @param <E>       泛型
     * @return 分页
     */
    public static <E> Page<E> page(Class<E> toConvert, int current, int size, String sql, Object... params) {
        Page<Map<String, Object>> page = page(current, size, sql, params);
        return Page.convert(
                page.getCurrent(),
                page.getSize(),
                page.getTotal(),
                write(toConvert, page.getRows())
        );
    }

    /**
     * 分页
     *
     * @param clazz   类
     * @param wrapper 封装
     * @param current 当前页码
     * @param size    每页条数
     * @param <E>     泛型
     * @return 分页
     */
    public static <E extends MysqlFactory> Page<E> page(Class<E> clazz, LambdaQueryWrapper<E> wrapper, int current, int size) {
        wrapper.offset(0).limit(0);
        long total = count(clazz, wrapper);
        wrapper.offset((current - 1) * size).limit(size);
        List<E> rows = find(clazz, wrapper);
        return Page.convert(current, size, total, rows);
    }

    /**
     * 批量保存
     *
     * @param collection 集合
     * @return 数量
     */
    public static <E extends MysqlFactory> long save(Collection<E> collection) {
        return Objects.isNull(collection) || collection.isEmpty() ? 0 : collection.stream().filter(it -> it.save()).count();
    }

    /**
     * 批量更新
     *
     * @param collection 集合
     * @return 数量
     */
    public static <E extends MysqlFactory> long update(Collection<E> collection) {
        return Objects.isNull(collection) || collection.isEmpty() ? 0 : collection.stream().filter(it -> it.update()).count();
    }

    /**
     * 批量保存或更新
     *
     * @param collection 集合
     * @return 数量
     */
    public static <E extends MysqlFactory> long saveOrUpdate(Collection<E> collection) {
        return Objects.isNull(collection) || collection.isEmpty() ? 0 : collection.stream().filter(it -> it.update()).count();
    }

    /**
     * 批量删除
     *
     * @param collection 实体集合
     * @return 数量
     */
    public static <E extends MysqlFactory> long delete(Collection<E> collection) {
        return Objects.isNull(collection) || collection.isEmpty() ? 0 : collection.stream().filter(it -> it.delete()).count();
    }

    /**
     * 更新
     *
     * @param sql    sql语句
     * @param params
     * @return
     */
    public static int update(String sql, Object... params) {
        return master.update(sql, params);
    }

    /**
     * 更新
     *
     * @param clazz   类
     * @param wrapper 封装
     * @param <E>     泛型
     * @return 成功数量
     */
    public static <E extends MysqlFactory> int update(Class<E> clazz, LambdaUpdateWrapper<E> wrapper) {
        BeanHelper helper = new BeanHelper(clazz);
        Mapper mapper = helper.getMapper();
        Collection<Field> fields = helper.getFields();
        if (Objects.isNull(mapper)) {
            throw new RuntimeException("对象未与存储结构进行映射");
        }
        List<Segment> update = wrapper.updateSegments();
        if (update.isEmpty()) {
            throw new RuntimeException("未设置更新参数");
        }
        Optional<Field> versionField = fields.stream()
                .filter(f -> f.isAnnotationPresent(Version.class))
                .findFirst();
        if (versionField.isPresent()) {
            update = update.stream().filter(s ->
                            !(s.getField() instanceof Field && ((Field) s.getField()).equals(versionField.get()))
                    )
                    .collect(Collectors.toList());
        }
        StringJoiner sj = new StringJoiner(SINGLE_STRING)
                .add(UPDATE)
                .add(String.format(SIMPLE_NAME, mapper.value()))
                .add(SET)
                .add(update.stream().map(it -> new StringJoiner(SINGLE_STRING)
                                .add(columnName(it.getField(), mapper.underline(), false))
                                .add(it.getOperator())
                                .add(QUESTION)
                                .toString()
                        ).collect(Collectors.joining(COMMA))
                );
        if (versionField.isPresent()) {
            sj.add(COMMA)
                    .add(columnName(versionField.get(), mapper.underline(), false))
                    .add(EQ)
                    .add(columnName(versionField.get(), mapper.underline(), false))
                    .add(ADD_ONE);
        }
        List<Object> params = update.stream().map(it -> it.getValue()).collect(Collectors.toList());
        if (Objects.nonNull(wrapper.whereSegment())
                && Objects.nonNull(wrapper.whereSegment().getChildren())
                && !wrapper.whereSegment().getChildren().isEmpty()) {
            sj.add(WHERE).add(where(mapper, wrapper.whereSegment(), false));
            params.addAll(params(wrapper.whereSegment()));
        }
        return update(sj.toString(), params.toArray());
    }

    /**
     * 删除
     *
     * @param clazz   类
     * @param wrapper 封装
     * @param <E>     泛型
     * @return 数量
     */
    public static <E extends MysqlFactory> int delete(Class<E> clazz, LambdaDeleteWrapper<E> wrapper) {
        BeanHelper helper = new BeanHelper(clazz);
        Mapper mapper = helper.getMapper();
        if (Objects.isNull(mapper)) {
            throw new RuntimeException("对象未与存储结构进行映射");
        }
        Optional<Field> deleteField = helper.getFields().stream()
                .filter(f -> f.isAnnotationPresent(LogicDelete.class))
                .findFirst();
        Collection<Object> params = new ArrayList<>();
        StringJoiner sj = new StringJoiner(SINGLE_STRING);
        if (deleteField.isPresent()) {
            sj.add(UPDATE)
                    .add(String.format(SIMPLE_NAME, mapper.value()))
                    .add(SET)
                    .add(columnName(deleteField.get(), mapper.underline(), false))
                    .add(EQ)
                    .add(QUESTION);
            params.add(1);
        } else {
            sj.add(DELETE).add(FROM).add(String.format(SIMPLE_NAME, mapper.value()));
        }
        Segment where = wrapper.whereSegment();
        if (Objects.nonNull(where) && Objects.nonNull(where.getChildren()) && !where.getChildren().isEmpty()) {
            sj.add(WHERE).add(where(mapper, where, false));
            params.addAll(params(where));
        }
        if (wrapper.limit() > 0) {
            sj.add(LIMIT);
            if (wrapper.offset() > 0) {
                sj.add(QUESTION).add(COMMA);
                params.add(wrapper.offset());
            }
            sj.add(QUESTION);
            params.add(wrapper.limit());
        }
        return update(sj.toString(), params.toArray());
    }

    /**
     * 保存
     *
     * @return 是否成功
     */
    @SneakyThrows
    @Override
    public boolean save() {
        BeanHelper helper = new BeanHelper(this.getClass());
        Mapper mapper = helper.getMapper();
        if (Objects.isNull(mapper)) {
            throw new RuntimeException("对象未与存储结构进行映射");
        }
        Collection<Field> fields = helper.getFields();
        Collection<Method> methods = helper.getMethods();
        fill(this, Fill.INSERT);
        Map<Field, Object> map = read(this);
        List<Field> columns = fields.stream().filter(f ->
                !f.isAnnotationPresent(Transient.class)
                        && !f.isAnnotationPresent(GenKey.class)
                        && !Modifier.isTransient(f.getModifiers())
                        && !Modifier.isStatic(f.getModifiers())
                        && !Modifier.isFinal(f.getModifiers())
                        && Objects.nonNull(map.get(f))
        ).collect(Collectors.toList());
        if (columns.isEmpty()) {
            throw new RuntimeException("无法存储空对象数据");
        }
        final StringJoiner sj = new StringJoiner(SINGLE_STRING)
                .add(INSERT)
                .add(String.format(SIMPLE_NAME, mapper.value())
                        + LEFT
                        + columns.stream().map(f -> columnName(f, mapper.underline(), false)).collect(Collectors.joining(COMMA))
                        + RIGHT
                ).add(VALUES + columns.stream().map(f -> QUESTION).collect(Collectors.joining(COMMA)) + RIGHT);
        String sql = sj.toString();
        List<Object> params = IntStream.range(0, columns.size()).mapToObj(i -> map.get(columns.get(i)))
                .collect(Collectors.toList());
        KeyHolder keyHolder = new GeneratedKeyHolder();
        int r = master.update(conn -> {
            PreparedStatement ps = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
            IntStream.range(0, params.size()).forEach(i -> {
                try {
                    ps.setObject(i + 1, params.get(i));
                } catch (SQLException e) {
                    throw new RuntimeException(e);
                }
            });
            return ps;
        }, keyHolder);
        Optional<Field> idField = fields.stream().filter(f -> f.isAnnotationPresent(GenKey.class)).findFirst();
        if (idField.isPresent()) {
            Optional<Method> method = methods.stream()
                    .filter(m -> m.getName().equals(SET + BeanHelper.toFirstUpperCase(idField.get().getName())))
                    .findFirst();
            if (method.isPresent()) {
                method.get().setAccessible(true);
                method.get().invoke(this, keyHolder.getKey().longValue());
            }
        }
        return r > 0;
    }

    /**
     * 更新
     *
     * @return 是否成功
     */
    @Override
    public boolean update() {
        BeanHelper helper = new BeanHelper(this.getClass());
        Mapper mapper = helper.getMapper();
        if (Objects.isNull(mapper)) {
            throw new RuntimeException("对象未与存储结构进行映射");
        }
        Collection<Field> fields = helper.getFields();
        Optional<Field> idField = fields.stream().filter(f -> f.isAnnotationPresent(ID.class)).findFirst();
        if (!idField.isPresent()) {
            throw new RuntimeException("未设置ID，无法执行");
        }
        Optional<Field> versionField = fields.stream()
                .filter(f -> f.isAnnotationPresent(Version.class))
                .findFirst();
        List<Field> columns = fields.stream().filter(f ->
                !f.isAnnotationPresent(Transient.class)
                        && !f.isAnnotationPresent(GenKey.class)
                        && !Modifier.isTransient(f.getModifiers())
                        && !Modifier.isStatic(f.getModifiers())
                        && !Modifier.isFinal(f.getModifiers())
                        && !f.equals(idField.get())
                        && !(versionField.isPresent() && f.equals(versionField.get()))
        ).collect(Collectors.toList());
        if (columns.isEmpty()) {
            throw new RuntimeException("无法更新空对象数据");
        }
        Optional<Field> deleteField = fields.stream().filter(f -> f.isAnnotationPresent(LogicDelete.class)).findFirst();
        if (deleteField.isPresent()) {
            columns.remove(deleteField.get());
        }
        fill(this, Fill.INSERT);
        Map<Field, Object> map = read(this);
        final StringJoiner sj = new StringJoiner(SINGLE_STRING)
                .add(UPDATE)
                .add(String.format(SIMPLE_NAME, mapper.value()))
                .add(SET)
                .add(columns.stream()
                        .map(f -> columnName(f, mapper.underline(), false) + EQ_QUESTION)
                        .collect(Collectors.joining(COMMA))
                );
        if (versionField.isPresent()) {
            sj.add(COMMA)
                    .add(columnName(versionField.get(), mapper.underline(), false))
                    .add(EQ)
                    .add(columnName(versionField.get(), mapper.underline(), false))
                    .add(ADD_ONE);
        }
        sj.add(WHERE).add(columnName(idField.get(), mapper.underline(), false) + EQ_QUESTION);
        if (versionField.isPresent()) {
            sj.add(AND).add(columnName(versionField.get(), mapper.underline(), false) + EQ_QUESTION);
        }
        if (deleteField.isPresent()) {
            sj.add(AND).add(columnName(deleteField.get(), mapper.underline(), false) + EQ_QUESTION);
        }
        String sql = sj.toString();
        List<Object> params = IntStream.range(0, columns.size())
                .mapToObj(i -> map.get(columns.get(i)))
                .collect(Collectors.toList());
        params.add(map.get(idField.get()));
        if (versionField.isPresent()) {
            params.add(map.get(versionField.get()));
        }
        if (deleteField.isPresent()) {
            params.add(0);
        }
        return master.update(sql, params.toArray()) > 0;
    }

    /**
     * 保存或更新
     *
     * @return 是否成功
     */
    @Override
    public boolean saveOrUpdate() {
        BeanHelper helper = new BeanHelper(this.getClass());
        Mapper mapper = helper.getMapper();
        if (Objects.isNull(mapper)) {
            throw new RuntimeException("对象未与存储结构进行映射");
        }
        Collection<Field> fields = helper.getFields();
        Optional<Field> idField = fields.stream().filter(f -> f.isAnnotationPresent(ID.class)).findFirst();
        if (!idField.isPresent()) {
            throw new RuntimeException("未设置ID，无法执行");
        }
        fill(this, Fill.UPDATE);
        Map<Field, Object> map = read(this);
        if (Objects.nonNull(map.get(idField.get())) && Objects.nonNull(get(this.getClass(), map.get(idField.get())))) {
            return this.update();
        }
        return this.save();
    }

    /**
     * 删除
     *
     * @return 是否成功
     */
    @Override
    public boolean delete() {
        BeanHelper helper = new BeanHelper(this.getClass());
        Mapper mapper = helper.getMapper();
        if (Objects.isNull(mapper)) {
            throw new RuntimeException("对象未与存储结构进行映射");
        }
        Collection<Field> fields = helper.getFields();
        Optional<Field> idField = fields.stream().filter(f -> f.isAnnotationPresent(ID.class)).findFirst();
        if (!idField.isPresent()) {
            throw new RuntimeException("未设置ID，无法执行");
        }
        fill(this, Fill.UPDATE);
        Map<Field, Object> map = read(this);
        Collection<Object> params;
        StringJoiner sj = new StringJoiner(SINGLE_STRING);
        Optional<Field> deleteField = fields.stream()
                .filter(f -> f.isAnnotationPresent(LogicDelete.class))
                .findFirst();
        Optional<Field> versionField = fields.stream()
                .filter(f -> f.isAnnotationPresent(Version.class))
                .findFirst();
        if (deleteField.isPresent()) {
            sj.add(UPDATE)
                    .add(String.format(SIMPLE_NAME, mapper.value()))
                    .add(SET)
                    .add(columnName(deleteField.get(), mapper.underline(), false) + EQ_QUESTION);
            if (versionField.isPresent()) {
                sj.add(COMMA)
                        .add(columnName(versionField.get(), mapper.underline(), false))
                        .add(EQ)
                        .add(columnName(versionField.get(), mapper.underline(), false))
                        .add(ADD_ONE);
            }
            sj.add(WHERE)
                    .add(columnName(idField.get(), mapper.underline(), false))
                    .add(EQ)
                    .add(QUESTION);
            params = new ArrayList<>();
            params.add(1);
            params.add(map.get(idField.get()));
            if (versionField.isPresent()) {
                sj.add(AND).add(columnName(versionField.get(), mapper.underline(), false) + EQ_QUESTION);
                params.add(map.get(versionField.get()));
            }
        } else {
            sj.add(DELETE)
                    .add(FROM)
                    .add(String.format(SIMPLE_NAME, mapper.value()))
                    .add(WHERE)
                    .add(columnName(idField.get(), mapper.underline(), false))
                    .add(EQ)
                    .add(QUESTION);
            params = Arrays.asList(map.get(idField.get()));
            if (versionField.isPresent()) {
                sj.add(AND).add(columnName(versionField.get(), mapper.underline(), false) + EQ_QUESTION);
                params.add(map.get(versionField.get()));
            }
        }
        String sql = sj.toString();
        return master.update(sql, params.toArray()) > 0;
    }

    /**
     * 获取参数
     *
     * @param segment 片段
     * @return 参数
     */
    private static Collection<Object> params(Segment segment) {
        Collection<Object> collection = new ArrayList<>();
        if (!isSubSegment(segment)) {
            if (Objects.isNull(segment.getValue())) {
                return null;
            }
            if (segment.getValue().getClass().isArray()) {
                return Arrays.asList(segment.getValue());
            } else if (segment.getValue() instanceof Collection) {
                return ((Collection) segment.getValue());
            }
            if (Arrays.asList(LIKE, NOT_LIKE).contains(segment.getOperator())) {
                String p = PER + segment.getValue().toString() + PER;
                return Arrays.asList(p);
            }
            return Arrays.asList(segment.getValue());
        }
        if (Objects.isNull(segment.getChildren()) || segment.getChildren().isEmpty()) {
            return Collections.EMPTY_LIST;
        }
        segment.getChildren().stream()
                .filter(it -> !(Objects.nonNull(it.getValue())
                        && it.getValue() instanceof String
                        && ((String) it.getValue()).startsWith(D))
                ).forEach(it -> {
                    Collection<Object> child = params(it);
                    if (Objects.nonNull(child)) {
                        collection.addAll(child);
                    }
                });
        return collection;
    }

    /**
     * select语句
     *
     * @param mapper    映射
     * @param selectors 字段
     * @param withJoin  是否管理
     * @return select 语句
     */
    private static String select(Mapper mapper, Collection<Field> selectors, boolean withJoin) {
        StringJoiner sj = new StringJoiner(SINGLE_STRING)
                .add(SELECT)
                .add(selectors.stream().map(f ->
                                columnName(f, mapper.underline(), withJoin) + AS + f.getName()
                        ).collect(Collectors.joining(COMMA))
                ).add(FROM)
                .add(withJoin ? String.format(ALL_NAME, mapper.value()) : String.format(SIMPLE_NAME, mapper.value()));
        return sj.toString();
    }

    /**
     * join 语句
     *
     * @param mapper 映射
     * @param joiner 关联
     * @param index  排序
     * @return join语句
     */
    private static String join(Mapper mapper, Segment joiner, int index) {
        Segment on = Segment.build(null, AND, null);
        on.append(joiner.getChildren());
        return new StringJoiner(SINGLE_STRING)
                .add(joiner.getOperator())
                .add(joiner.getField().toString())
                .add(D + index)
                .add(ON)
                .add(where(mapper, on, false))
                .toString();
    }

    /**
     * where语句
     *
     * @param mapper   存储映射
     * @param segment  片段
     * @param withJoin 是否关联查询
     * @return where 语句
     */
    private static String where(Mapper mapper, Segment segment, boolean withJoin, boolean... display) {
        if (OR.equals(segment.getOperator())) {
            return null;
        }
        if (!isSubSegment(segment)) {
            return new StringJoiner(SINGLE_STRING)
                    .add(columnName(segment.getField(), mapper.underline(), withJoin))
                    .add(segment.getOperator())
                    .add(placeholder(segment.getValue()))
                    .toString();
        }
        if (Objects.isNull(segment.getChildren()) || segment.getChildren().isEmpty()) {
            return null;
        }
        final String[] delimiter = {AND};
        if (segment.getChildren().stream().filter(s -> OR.equals(s.getOperator())).count() > 0) {
            delimiter[0] = OR;
        }
        String w = segment.getChildren().stream().filter(s -> !OR.equals(s.getOperator()))
                .map(s -> where(mapper, s, withJoin, true))
                .filter(Objects::nonNull)
                .collect(Collectors.joining(SINGLE_STRING + delimiter[0] + SINGLE_STRING));
        boolean d = Objects.nonNull(display) && display.length > 0 && display[0];
        return d ? new StringJoiner(EMPTY).add(LEFT).add(w).add(RIGHT).toString() : w;
    }

    /**
     * order 语句
     *
     * @param mapper   映射
     * @param segments 片段
     * @return order 语句
     */
    private static String orderBy(Mapper mapper, List<Segment> segments, boolean withJoin) {
        return segments.stream()
                .map(it -> {
                    String field = it.getField().toString();
                    if (it.getField() instanceof Field) {
                        field = columnName(it.getField(), mapper.underline(), withJoin);
                    }
                    String sort = ASC;
                    if (Objects.nonNull(it.getValue()) && it.getValue() instanceof Boolean) {
                        sort = (Boolean) it.getValue() ? ASC : DESC;
                    }
                    return new StringJoiner(SINGLE_STRING).add(field).add(sort).toString();
                }).collect(Collectors.joining(COMMA));
    }

    /**
     * 是否子片段
     *
     * @param segment 判断
     * @return Boolean
     */
    private static boolean isSubSegment(Segment segment) {
        return AND.equals(segment.getOperator())
                || segment.getOperator().contains(JOIN);
    }

    /**
     * 占位符
     *
     * @param value 值
     * @return 占位符
     */
    private static String placeholder(Object value) {
        List<Object> places = new ArrayList<>();
        if (value.getClass().isArray()) {
            List<Object> values = Arrays.asList(value);
            places.addAll(values);
        } else if (value instanceof Collection) {
            Collection values = ((Collection) value);
            places.addAll(values);
        } else {
            return value instanceof String && value.toString().trim().startsWith(D) ? value.toString() : QUESTION;
        }
        String s = places.stream()
                .map(it -> it instanceof String && it.toString().trim().startsWith(D) ? value.toString() : QUESTION)
                .collect(Collectors.joining(COMMA));
        return places.size() > 1 ? new StringJoiner(EMPTY).add(LEFT).add(s).add(RIGHT).toString() : s;
    }

    /**
     * 填充
     *
     * @param that 对象
     * @param fill 填充
     */
    private static void fill(Object that, Fill fill) {
        BeanHelper helper = new BeanHelper(that.getClass());
        final Collection<Field> fields = helper.getFields();
        final Collection<Method> methods = helper.getMethods();
        fields.stream().filter(f -> f.isAnnotationPresent(MyField.class)
                && fill.equals(f.getAnnotation(MyField.class).fill())
        ).forEach(f -> {
            MyField myField = f.getAnnotation(MyField.class);
            Optional<Method> setter = methods.stream()
                    .filter(m -> m.getName().equals(SET + BeanHelper.toFirstUpperCase(f.getName())))
                    .findFirst();
            if (setter.isPresent()) {
                setter.get().setAccessible(true);
                try {
                    Object value = myField.using().newInstance().handle(that, f);
                    setter.get().invoke(that, value);
                } catch (Exception e) {
                    log.error("fill error ==>", e);
                }
            }
        });
    }

    /**
     * 读取对象字段与值
     *
     * @param that 对象
     * @return 字段 与 值
     */
    private static Map<Field, Object> read(Object that) {
        BeanHelper helper = new BeanHelper(that.getClass());
        final Collection<Field> fields = helper.getFields();
        final Collection<Method> methods = helper.getMethods();
        final Map<Field, Object> map = new HashMap<>(fields.size());
        fields.stream().forEach(f -> {
            Optional<Method> getter = methods.stream()
                    .filter(m -> m.getName().equals(GET + BeanHelper.toFirstUpperCase(f.getName()))
                            || m.getName().equals(IS + BeanHelper.toFirstUpperCase(f.getName())))
                    .findFirst();
            if (!getter.isPresent()) {
                return;
            }
            getter.get().setAccessible(true);
            Object value = null;
            try {
                value = getter.get().invoke(that);
            } catch (Exception e) {
                log.error("read error ==>", e);
            }
            if (Objects.isNull(value)) {
                return;
            }
            boolean toJsonString = f.isAnnotationPresent(MyField.class) && f.getAnnotation(MyField.class).toJsonString();
            map.put(f, format(value, toJsonString));
        });
        return map;
    }

    /**
     * 写入
     *
     * @param toConvert 类型
     * @param data      数据
     * @param <E>       泛型
     * @return 列表
     */
    private static <E> List<E> write(Class<E> toConvert, List<Map<String, Object>> data) {
        return data.stream().map(it -> parse(toConvert, it)).collect(Collectors.toList());
    }

    /**
     * 格式化
     *
     * @param value        值
     * @param toJsonString 是否转成json字符串
     * @return 值
     */
    @SneakyThrows
    private static Object format(Object value, boolean toJsonString) {
        if (value.getClass().isEnum()) {
            BeanHelper helper = new BeanHelper(value.getClass());
            final Collection<Field> fields = helper.getFields();
            final Collection<Method> methods = helper.getMethods();
            Optional<Field> field = fields.stream()
                    .filter(f -> f.isAnnotationPresent(EnumValue.class))
                    .findFirst();
            if (!field.isPresent()) {
                return value;
            }
            Optional<Method> getter = methods.stream()
                    .filter(m -> m.getName().equals(GET + BeanHelper.toFirstUpperCase(field.get().getName()))
                            || m.getName().equals(IS + BeanHelper.toFirstUpperCase(field.get().getName())))
                    .findFirst();
            if (!getter.isPresent()) {
                return value;
            }
            return getter.get().invoke(value);
        }
        if (toJsonString) {
            return JSON.toJSONString(value);
        }
        return value;
    }

    /**
     * 解析
     *
     * @param toConvert 类型
     * @param map       HashMap
     * @param <E>       泛型
     * @return 对象
     */
    @SneakyThrows
    private static <E> E parse(Class<E> toConvert, Map<String, Object> map) {
        BeanHelper helper = new BeanHelper(toConvert);
        Mapper mapper = helper.getMapper();
        Collection<Field> fields = helper.getFields();
        Collection<Method> methods = helper.getMethods();
        E entity = toConvert.newInstance();
        fields.stream().filter(f -> !f.isAnnotationPresent(Transient.class)
                        && !Modifier.isTransient(f.getModifiers())
                        && !Modifier.isStatic(f.getModifiers())
                        && !Modifier.isFinal(f.getModifiers()))
                .forEach(f -> {
                    String fieldName = f.getName();
                    Object value = convert(f, map.get(fieldName));
                    if (Objects.isNull(value)) {
                        fieldName = columnName(f, Objects.nonNull(mapper) ? mapper.underline() : true, false);
                        fieldName = fieldName.replace(LIMITER, EMPTY);
                        value = convert(f, map.get(fieldName));
                    }
                    if (Objects.isNull(value)) {
                        return;
                    }
                    Optional<Method> setter = methods.stream()
                            .filter(m -> m.getName().equals(SET + BeanHelper.toFirstUpperCase(f.getName())))
                            .findFirst();
                    if (setter.isPresent()) {
                        setter.get().setAccessible(true);
                        try {
                            setter.get().invoke(entity, value);
                        } catch (Exception e) {
                            log.error("{} => {}, parse error ==>", f, value, e);
                        }
                    }
                });
        return entity;
    }

    /**
     * 类型转化
     *
     * @param field 字段
     * @param obj   值
     * @return 类
     */
    private static Object convert(Field field, Object obj) {
        if (field.isAnnotationPresent(MyField.class) && field.getAnnotation(MyField.class).toJsonString()) {
            Class fieldClass = fieldClass(field);
            if (isArrayOrCollection(field.getType())) {
                return JSON.parseArray(JSON.toJSONString(obj), fieldClass);
            }
            return JSON.parseObject(JSON.toJSONString(obj), fieldClass);
        }
        return convert(field.getType(), obj);
    }

    /**
     * 类型转化
     *
     * @param clazz 类型
     * @param obj   值
     * @return 结果
     */
    private static Object convert(Class clazz, Object obj) {
        if (Objects.isNull(obj)) {
            return null;
        }
        if (clazz.isEnum()) {
            Object[] enums = clazz.getEnumConstants();
            BeanHelper helper = new BeanHelper(clazz);
            Collection<Field> fields = helper.getFields();
            Collection<Method> methods = helper.getMethods();
            Optional<Field> field = fields.stream()
                    .filter(f -> f.isAnnotationPresent(EnumValue.class))
                    .findFirst();
            if (!field.isPresent()) {
                return null;
            }
            Optional<Method> method = methods.stream()
                    .filter(m -> m.getName().equals(GET + BeanHelper.toFirstUpperCase(field.get().getName())))
                    .findFirst();
            if (!method.isPresent()) {
                return null;
            }
            Optional<Object> opt = Arrays.asList(enums).stream()
                    .filter(o -> {
                        try {
                            return method.get().invoke(o).equals(obj);
                        } catch (Exception e) {
                            log.error("getter error => ", e);
                        }
                        return false;
                    }).findFirst();
            return opt.isPresent() ? opt.get() : null;
        }
        if (String.class.getName().equals(clazz.getName())) {
            return obj.toString();
        }
        if (Boolean.class.getName().equals(clazz.getName())) {
            return obj.equals(0) ? false : true;
        }
        if (Integer.class.getName().equals(clazz.getName())) {
            return ((Number) obj).intValue();
        }
        if (Long.class.getName().equals(clazz.getName())) {
            return ((Number) obj).longValue();
        }
        if (BigDecimal.class.getName().equals(clazz.getName())) {
            return new BigDecimal(obj.toString());
        }
        if (Byte.class.getName().equals(clazz.getName())) {
            return ((Number) obj).byteValue();
        }
        if (Float.class.getName().equals(clazz.getName())) {
            return ((Number) obj).floatValue();
        }
        if (Double.class.getName().equals(clazz.getName())) {
            return ((Number) obj).doubleValue();
        }
        if (Short.class.getName().equals(clazz.getName())) {
            return ((Number) obj).shortValue();
        }
        if (Date.class.getName().equals(clazz.getName())) {
            return (Date) obj;
        }
        return obj;
    }

    /**
     * 获取列名
     *
     * @param field     字段
     * @param underline 是否转下划线格式
     * @return 列名
     */
    private static String columnName(Object field, boolean underline, boolean withJoin) {
        if (Objects.isNull(field)) {
            return EMPTY;
        }
        if (field instanceof String) {
            return underline ? BeanHelper.toUnderline(field.toString()) : field.toString();
        }
        String col;
        Field f = (Field) field;
        if (!f.isAnnotationPresent(MyField.class)) {
            col = underline ? BeanHelper.toUnderline(f.getName()) : f.getName();
        } else {
            MyField myField = f.getAnnotation(MyField.class);
            col = myField.value();
        }
        if (col.startsWith(LIMITER)) {
            col = col.replace(LIMITER, EMPTY);
        }
        return withJoin ? String.format(ALL_NAME, col) : String.format(SIMPLE_NAME, col);
    }

    /**
     * 获取类型
     *
     * @param field 字段
     * @return 字段类型
     */
    private static Class fieldClass(Field field) {
        Class clazz = field.getType();
        if (isArrayOrCollection(clazz)) {
            String classname = ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0].getTypeName();
            try {
                clazz = Class.forName(classname);
            } catch (ClassNotFoundException e) {
                return Map.class;
            }
        }
        return clazz;
    }

    /**
     * 是否数组或者集合
     *
     * @param type 类型
     * @return 是否数组或者集合
     */
    private static boolean isArrayOrCollection(Class type) {
        //判断返回类型是否是集合类型
        boolean isCollection = Collection.class.isAssignableFrom(type);
        //判断返回类型是否是数组类型
        boolean isArray = type.isArray();
        return isCollection || isArray;
    }

}
